<template>
  <div class="nb-catalog">
    <!-- 搜索栏 -->
    <div v-if="searchable" class="nb-catalog-search">
      <el-input
        v-model="searchText"
        :placeholder="searchPlaceholder"
        clearable
        @input="handleSearch"
      >
        <template #prefix>
          <el-icon><Search /></el-icon>
        </template>
      </el-input>
    </div>

    <!-- 工具栏 -->
    <div v-if="toolbar" class="nb-catalog-toolbar">
      <el-button 
        v-if="addable" 
        type="primary" 
        size="small"
        @click="handleAdd"
      >
        <el-icon><Plus /></el-icon>
        {{ addText }}
      </el-button>
      <el-button 
        v-if="expandable"
        size="small"
        @click="toggleExpandAll"
      >
        <el-icon><component :is="isExpandAll ? 'Fold' : 'Expand'" /></el-icon>
        {{ isExpandAll ? '全部折叠' : '全部展开' }}
      </el-button>
    </div>

    <!-- 目录树 -->
    <el-tree
      ref="treeRef"
      :data="filteredData"
      :props="treeProps"
      :node-key="nodeKey"
      :default-expand-all="defaultExpandAll"
      :expand-on-click-node="expandOnClickNode"
      :check-on-click-node="checkOnClickNode"
      :show-checkbox="showCheckbox"
      :draggable="draggable"
      :allow-drop="allowDrop"
      :allow-drag="allowDrag"
      :highlight-current="highlightCurrent"
      :filter-node-method="filterNode"
      @node-click="handleNodeClick"
      @node-contextmenu="handleNodeContextmenu"
      @check="handleCheck"
      @node-expand="handleNodeExpand"
      @node-collapse="handleNodeCollapse"
      @node-drag-start="handleDragStart"
      @node-drag-end="handleDragEnd"
      @node-drop="handleDrop"
    >
      <template #default="{ node, data }">
        <div class="nb-catalog-node">
          <!-- 节点图标 -->
          <el-icon v-if="data.icon" class="node-icon">
            <component :is="data.icon" />
          </el-icon>
          
          <!-- 节点内容 -->
          <span v-if="!data.editing" class="node-label" @dblclick="handleEdit(node, data)">
            {{ node.label }}
          </span>
          
          <!-- 编辑输入框 -->
          <el-input
            v-else
            v-model="data.editLabel"
            size="small"
            @blur="handleSaveEdit(node, data)"
            @keyup.enter="handleSaveEdit(node, data)"
            @keyup.esc="handleCancelEdit(node, data)"
          />

          <!-- 节点徽章 -->
          <el-badge 
            v-if="data.badge" 
            :value="data.badge"
            class="node-badge"
          />

          <!-- 节点标签 -->
          <el-tag 
            v-if="data.tag"
            :type="data.tagType || 'info'"
            size="small"
            class="node-tag"
          >
            {{ data.tag }}
          </el-tag>

          <!-- 操作按钮 -->
          <div v-if="editable && !data.editing" class="node-actions">
            <el-button
              v-if="addable"
              type="primary"
              size="small"
              text
              @click.stop="handleAddChild(node, data)"
            >
              <el-icon><Plus /></el-icon>
            </el-button>
            <el-button
              type="primary"
              size="small"
              text
              @click.stop="handleEdit(node, data)"
            >
              <el-icon><Edit /></el-icon>
            </el-button>
            <el-button
              v-if="deletable"
              type="danger"
              size="small"
              text
              @click.stop="handleDelete(node, data)"
            >
              <el-icon><Delete /></el-icon>
            </el-button>
          </div>
        </div>
      </template>
    </el-tree>

    <!-- 空状态 -->
    <el-empty 
      v-if="filteredData.length === 0"
      :description="emptyText"
    />
  </div>
</template>

<script setup>
import { ref, computed, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { 
  Search, 
  Plus, 
  Edit, 
  Delete, 
  Expand, 
  Fold 
} from '@element-plus/icons-vue'

const props = defineProps({
  // 目录数据
  data: {
    type: Array,
    default: () => []
  },
  // 树节点配置
  treeProps: {
    type: Object,
    default: () => ({
      children: 'children',
      label: 'label'
    })
  },
  // 节点唯一标识
  nodeKey: {
    type: String,
    default: 'id'
  },
  // 是否显示搜索框
  searchable: {
    type: Boolean,
    default: true
  },
  // 搜索框占位符
  searchPlaceholder: {
    type: String,
    default: '搜索目录'
  },
  // 是否显示工具栏
  toolbar: {
    type: Boolean,
    default: true
  },
  // 是否可添加
  addable: {
    type: Boolean,
    default: true
  },
  // 添加按钮文本
  addText: {
    type: String,
    default: '添加'
  },
  // 是否可编辑
  editable: {
    type: Boolean,
    default: true
  },
  // 是否可删除
  deletable: {
    type: Boolean,
    default: true
  },
  // 是否可拖拽
  draggable: {
    type: Boolean,
    default: false
  },
  // 是否显示复选框
  showCheckbox: {
    type: Boolean,
    default: false
  },
  // 默认展开所有节点
  defaultExpandAll: {
    type: Boolean,
    default: false
  },
  // 是否支持展开折叠
  expandable: {
    type: Boolean,
    default: true
  },
  // 点击节点时是否展开
  expandOnClickNode: {
    type: Boolean,
    default: false
  },
  // 点击节点时是否选中
  checkOnClickNode: {
    type: Boolean,
    default: false
  },
  // 高亮当前节点
  highlightCurrent: {
    type: Boolean,
    default: true
  },
  // 空数据文本
  emptyText: {
    type: String,
    default: '暂无数据'
  }
})

const emit = defineEmits([
  'node-click',
  'node-contextmenu',
  'check',
  'add',
  'edit',
  'delete',
  'drag-start',
  'drag-end',
  'drop',
  'node-expand',
  'node-collapse'
])

// 树组件引用
const treeRef = ref(null)

// 搜索文本
const searchText = ref('')

// 是否全部展开
const isExpandAll = ref(props.defaultExpandAll)

// 过滤后的数据
const filteredData = computed(() => {
  if (!searchText.value) {
    return props.data
  }
  return filterTree(props.data, searchText.value)
})

// 过滤树节点
const filterTree = (tree, keyword) => {
  const result = []
  tree.forEach(node => {
    const label = node[props.treeProps.label] || ''
    if (label.toLowerCase().includes(keyword.toLowerCase())) {
      result.push({ ...node })
    } else if (node[props.treeProps.children]) {
      const children = filterTree(node[props.treeProps.children], keyword)
      if (children.length > 0) {
        result.push({
          ...node,
          [props.treeProps.children]: children
        })
      }
    }
  })
  return result
}

// 节点过滤方法
const filterNode = (value, data) => {
  if (!value) return true
  const label = data[props.treeProps.label] || ''
  return label.toLowerCase().includes(value.toLowerCase())
}

// 搜索处理
const handleSearch = () => {
  if (treeRef.value) {
    treeRef.value.filter(searchText.value)
  }
}

// 切换全部展开/折叠
const toggleExpandAll = () => {
  isExpandAll.value = !isExpandAll.value
  if (treeRef.value) {
    const nodes = treeRef.value.store._getAllNodes()
    nodes.forEach(node => {
      node.expanded = isExpandAll.value
    })
  }
}

// 节点点击
const handleNodeClick = (data, node) => {
  emit('node-click', data, node)
}

// 节点右键菜单
const handleNodeContextmenu = (event, data, node) => {
  emit('node-contextmenu', event, data, node)
}

// 复选框选中
const handleCheck = (data, checked) => {
  emit('check', data, checked)
}

// 添加根节点
const handleAdd = () => {
  emit('add', null)
}

// 添加子节点
const handleAddChild = (node, data) => {
  emit('add', data)
}

// 编辑节点
const handleEdit = (node, data) => {
  data.editing = true
  data.editLabel = data[props.treeProps.label]
}

// 保存编辑
const handleSaveEdit = (node, data) => {
  if (data.editLabel && data.editLabel.trim()) {
    data[props.treeProps.label] = data.editLabel.trim()
    emit('edit', data)
  }
  data.editing = false
  delete data.editLabel
}

// 取消编辑
const handleCancelEdit = (node, data) => {
  data.editing = false
  delete data.editLabel
}

// 删除节点
const handleDelete = async (node, data) => {
  try {
    await ElMessageBox.confirm(
      '确定要删除该节点吗？',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }
    )
    emit('delete', data, node)
  } catch {
    // 用户取消
  }
}

// 拖拽开始
const handleDragStart = (node, event) => {
  emit('drag-start', node, event)
}

// 拖拽结束
const handleDragEnd = (draggingNode, dropNode, dropType, event) => {
  emit('drag-end', draggingNode, dropNode, dropType, event)
}

// 拖拽放置
const handleDrop = (draggingNode, dropNode, dropType, event) => {
  emit('drop', draggingNode, dropNode, dropType, event)
}

// 节点展开
const handleNodeExpand = (data, node) => {
  emit('node-expand', data, node)
}

// 节点折叠
const handleNodeCollapse = (data, node) => {
  emit('node-collapse', data, node)
}

// 允许拖拽
const allowDrag = (draggingNode) => {
  return true
}

// 允许放置
const allowDrop = (draggingNode, dropNode, type) => {
  return true
}

// 获取选中的节点
const getCheckedNodes = () => {
  return treeRef.value ? treeRef.value.getCheckedNodes() : []
}

// 获取选中的节点 key
const getCheckedKeys = () => {
  return treeRef.value ? treeRef.value.getCheckedKeys() : []
}

// 设置选中的节点
const setCheckedKeys = (keys) => {
  if (treeRef.value) {
    treeRef.value.setCheckedKeys(keys)
  }
}

// 获取当前节点
const getCurrentNode = () => {
  return treeRef.value ? treeRef.value.getCurrentNode() : null
}

// 设置当前节点
const setCurrentKey = (key) => {
  if (treeRef.value) {
    treeRef.value.setCurrentKey(key)
  }
}

// 暴露方法
defineExpose({
  getCheckedNodes,
  getCheckedKeys,
  setCheckedKeys,
  getCurrentNode,
  setCurrentKey
})
</script>

<style scoped>
.nb-catalog {
  background: #fff;
  border-radius: 4px;
  padding: 16px;
}

.nb-catalog-search {
  margin-bottom: 16px;
}

.nb-catalog-toolbar {
  margin-bottom: 16px;
  display: flex;
  gap: 8px;
}

.nb-catalog-node {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  padding-right: 8px;
}

.node-icon {
  color: #409eff;
}

.node-label {
  flex: 1;
  font-size: 14px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.node-badge {
  margin-right: 8px;
}

.node-tag {
  margin-right: 8px;
}

.node-actions {
  display: none;
  gap: 4px;
}

.nb-catalog-node:hover .node-actions {
  display: flex;
}

:deep(.el-tree-node__content) {
  height: 36px;
}

:deep(.el-tree-node__content:hover) {
  background-color: #f5f7fa;
}
</style>

